﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using Bule.Malbilden.Data;

namespace Bule.Malbilden.Domain
{
    public class Person : IPersistable
    {
        private GoalCollection goals = new GoalCollection();
        private IList<IEaten> eatenItems = new List<IEaten>();

        public Person()
            : this("unknown", 0, false)
        {
            IsInitialized = false;
        }

        public Person(string name, int age, bool isFemale)
            : this(name, age, isFemale, false, false) { }

        public Person(string name, int age, bool isFemale, bool isNursing, bool isPregnant)
        {
            if (!isFemale && (isNursing || isPregnant))
                throw new InvalidPersonException();

            CheckName(name);
            CheckAge(age);

            Age = age;
            Name = name;
            IsFemale = isFemale;
            IsNursing = isNursing;
            IsPregnant = isPregnant;
            IsInitialized = true;
        }

        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual bool IsFemale { get; set; }
        public virtual bool IsPregnant { get; set; }
        public virtual bool IsNursing { get; set; }
        public virtual bool IsInitialized { get; private set; }
        public virtual int Age { get; set; }

        protected IList<DayNutritionGoal> GoalsCollection
        {
            get { return goals; }
            set { goals = new GoalCollection(value); }
        }

        public virtual ReadOnlyCollection<DayNutritionGoal> Goals
        {
            get { return goals.ToList<DayNutritionGoal>().AsReadOnly(); }
        }

        public virtual ReadOnlyCollection<IEaten> EatenItem
        {
            get { return new ReadOnlyCollection<IEaten>(eatenItems); }
        }

        public virtual void AddNutritionGoal(DayNutritionGoal goal)
        {
            goals.AddOrReplace(goal);
        }

        public virtual void AddEatenItem(IEaten food)
        {
            eatenItems.Add(food);
        }

        public virtual DayNutritionGoal GetLatestNutritionGoal()
        {
            return goals.GetLatest();
        }

        public virtual DayNutritionGoal GetNutritionGoal(DateTime date)
        {
            return goals.Get(date);
        }

        public virtual DayNutritionGoal GetClosestGoal(DateTime date)
        {
            return goals.GetClosest(date);
        }

        public virtual IEnumerable<IEaten> GetEatenItem(DateTime date)
        {
            return from IEaten food in eatenItems
                   where food.Date == date
                   select food;
        }

        public virtual NutritionData GetTotalEatenNutritionalValue(DateTime date)
        {
            var query = from IEaten food in eatenItems
                        where food.Date == date
                        select food.TotalNutritionalValue;

            NutritionData total = new NutritionData();

            foreach (var nv in query)
                total += nv;

            return total;
        }

        public virtual void RemoveEatenItem(IEaten item)
        {
            eatenItems.Remove(item);
        }

        private void CheckName(string name)
        {
            if (string.IsNullOrEmpty(name))
                throw new InvalidPersonException();
        }

        private void CheckAge(int age)
        {
            if (age < 0)
                throw new InvalidPersonException();
        }

        // override object.Equals
        public override bool Equals(object obj)
        {
            bool areEqual = true;

            if (obj == null || GetType() != obj.GetType())
                return false;

            var right = (Person)obj;

            areEqual = goals.Count == right.goals.Count;

            foreach (var goal in goals)
            {
                var query = from DayNutritionGoal g in right.goals where goal.Equals(g) select g;
                areEqual = areEqual && query.Count() > 0;
            }

            areEqual = areEqual &&
                       Name == right.Name && Age == right.Age && IsFemale == right.IsFemale &&
                       IsNursing == right.IsNursing && IsPregnant == right.IsPregnant;

            return areEqual;
        }

        public override int GetHashCode()
        {
            return Name.GetHashCode();
        }
    }
}
